/*! \mainpage textTool v1.0
 *
 * \section intro_sec Introduction
 *
 * textTool is a program that can do several useful operation in a text file, like:
 *   - show the lines with a given word from a file;
 *   - show the word histogram from a file;
 *   - cypher or dechypher a file from a given table;
 *   - run a spell checker;
 *   - show a final word histogram from a folder;
 *   - Build a list of errors found in a text file in a folder.
 *
 */

/**
 * @file main.c
 * @brief TexTool. Advanced Programming Project
 * @version 1.0
 * @date Out-2011
 * @author Leonardo Lino 2101101
 * @author Joaquim Ferreira 2101104
 */

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#include "cmdline.h"
#include "crypt.h"
#include "debug.h"
#include "files.h"
#include "hashtables.h"
#include "listas.h"
#include "memory.h"
#include "pthrds.h"
#include "signal_out.h"
#include "str_utils.h"
#include "spell.h"
#include "toperrors.h"


/** @brief ERR_IO 1 - A constant that means that an error occurred.*/
#define ERR_IO 1
/** @brief CYPHER 1 - A constant to use for define a type.*/
#define CYPHER 1
/** @brief DECYPHER 2 - A constant to use for define a type.*/
#define DECYPHER 2
/** @brief WORD_OPTION 1 - A constant to use for define a type.*/
#define WORD_OPTION 1
/** @brief FREQ_OPTION 2 - A constant to use for define a type.*/
#define FREQ_OPTION 2
/** @brief SPELL_OPTION 3 - A constant to use for define a type. */
#define SPELL_OPTION 3
/** @brief MAX_THR_ERR 1 - A constant that means that the value (max_threads) is invalid.*/
#define MAX_THR_ERR 1

struct gengetopt_args_info args;

/** @brief This global integer variable (G_continue) will be used to treat the signal breakpoint to free all the resources.*/
int G_continue = 1;
/** @brief This global integer variable (G_proc_lines) will contain the number of processed lines.*/
int G_proc_lines = 0;
/** @brief This global integer variable (G_proc_files) will contain the number of processed files.*/
int G_proc_files = 0;


/**
 * @brief The main function.
 * @fn int main (int argc, char *argv[])
 * @param argc The number of arguments.
 * @param argv[] The pointer of pointers to the strings of the arguments.
 * @return Returns 0 if the program ends well, otherwise an error occurred.
 */
int main (int argc, char *argv[])
{
  /** @brief Disable warnings.*/
  (void) argc;
  (void) argv;

  /** @brief Variable declaration.*/
  struct sigaction act;
  time_t t_start;
  struct tm *time_start;

  /** @brief Main code.*/
  
  /** @brief Get the starting time.*/
  time (&t_start);
  time_start = gmtime(&t_start);

  if (cmdline_parser (argc, argv, &args) != 0) {
    cmdline_parser_free (&args);
    exit (ERR_IO);
  }

  /** @brief No input arguments.*/
  if (argc == 1)
    printf ("Missing arguments. Usage %s --help.\n", *argv);

  /** @brief Routine that treats the signal.*/
  act.sa_handler = signal_routine;

  /** @brief Mask without signals, it not lock.*/
  sigemptyset (&act.sa_mask);

  /** @brief Default -- reliable.*/
  act.sa_flags = 0;
  /** @brief retreaves blocking calls.*/
  act.sa_flags |= SA_RESTART;

  /** @brief Captures the signal SIGINT.*/
  if (sigaction (SIGINT, &act, NULL) < 0) {
    ERROR (1, "sigaction - SIGINT");
  }

  /** @brief Output option.*/
  if (args.output_given) {
    char *filename = args.output_arg;
    int fd;
    /**< @brief Mode options when creating a file.*/
    mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;

    /**< @brief Open a file, returning it's file descriptor.*/
    if ((fd = open (filename, O_CREAT | O_RDWR, mode)) == -1) {
      ERROR (ERR_IO, "Unable to open the file %s ", filename); /**< @brief An error occurred.*/
    } else {
      /**< @brief Closing the stdout.*/
      close (1);
      /**< @brief Duplicate a file descriptor to the file above.*/
      dup (fd);
      /**< @brief Now the stdout is redirected to the file.*/
      close (fd);
    }
  }

  /** @brief Word option.*/
  if (args.word_given && args.inputs_num != 1) {
    invalid_param (*argv, "--word <word>");
  } else {
    if (args.word_given && args.inputs_num == 1) {
      char *word = args.word_arg;
      char *filename = args.inputs[0];
      char *filetxt = NULL;
      char *str_opt = NULL;
	  
      if (validateWord (word))
        word = strlower (word);

      if (check_file_type (filename, ".pdf")) {
	filetxt = convert_pdf (filename);
	getLinesOrWords (filetxt, word, WORD_OPTION);
	rem_filetxt (filetxt);
	MY_FREE (filetxt);
      } else {
	getLinesOrWords (filename, word, WORD_OPTION);
      }
      
    str_opt =  (char *) MY_MALLOC (sizeof (char) * (strlen (word) + strlen (filename) + 9));
    sprintf (str_opt, "--word %s %s", word, filename);
    out_stderr (str_opt, t_start);
    MY_FREE (str_opt);
    }
  }

  /** @brief Freq option.*/
  if (args.freq_given) {
    char *filename = args.freq_arg;
    HASHTABLE_T *f_word_hasht = NULL;
    char *str_opt = NULL;

    /**< @brief Create the hashtable.*/
    f_word_hasht = tabela_criar (101, (LIBERTAR_FUNC) free_freq_word);
    /**< @brief Get the hashtable with keys (words) and the correspondent values (words and counters) from the filename.*/
    f_word_hasht = getLinesOrWords (filename, f_word_hasht, FREQ_OPTION);
    /**< @brief Convert the hashtable to a linkedlist and show the word histogram.*/
    convert_show_histogram (f_word_hasht);
    
    str_opt =  (char *) MY_MALLOC (sizeof (char) * (strlen (filename) + 8));
    sprintf (str_opt, "--freq %s",filename);
    out_stderr (str_opt, t_start);
    MY_FREE (str_opt);
  }

  /** @brief Cypher option.*/
  if (args.cypher_given && args.inputs_num != 1) {
    invalid_param (*argv, "--cypher <tablename.txt>");
  } else {
    if (args.cypher_given && args.inputs_num == 1) {
      char *table = args.cypher_arg;
      char *filename = args.inputs[0];
      char *str_opt = NULL;

      cypher (table, filename, CYPHER);
      
      str_opt =  (char *) MY_MALLOC (sizeof (char) * (strlen (table) + strlen (filename) + 11));
      sprintf (str_opt, "--cypher %s %s", table, filename);
      out_stderr (str_opt, t_start);
      MY_FREE (str_opt);
    }
  }

  /** @brief Decypher option.*/
  if (args.decypher_given && args.inputs_num != 1) {
    invalid_param (*argv, "--decypher <tablename.txt>");
  } else {
    if (args.decypher_given && args.inputs_num == 1) {
      char *table = args.decypher_arg;
      char *filename = args.inputs[0];
      char *str_opt = NULL;

      cypher (table, filename, DECYPHER);
      
      str_opt =  (char *) MY_MALLOC (sizeof (char) * (strlen (table) + strlen (filename) + 13));
      sprintf (str_opt, "--decypher %s %s", table, filename);
      out_stderr (str_opt, t_start);
      MY_FREE (str_opt);
    }
  }

  /** @brief Spell option.*/
  if (args.spell_given) {
    if (args.spell_given && args.inputs_num != 1) {
      invalid_param (*argv, "--spell <dictionary.txt>");
    } else {
      if (args.spell_given && args.inputs_num == 1) {
	char *dictionary = args.spell_arg;
	char *filename = args.inputs[0];
	char *str_opt = NULL;
	
	mainSpell(dictionary, filename);
	
	str_opt =  (char *) MY_MALLOC (sizeof (char) * (strlen (dictionary) + strlen (filename) + 10));
        sprintf (str_opt, "--spell %s %s", dictionary, filename);
        out_stderr (str_opt, t_start);
        MY_FREE (str_opt);
      }
    }
  }

  /** @brief Freq folder option.*/
  if (args.freq_folder_given && args.freq_max_threads_given) {
    char *folder_arg = args.freq_folder_arg;
    int f_max_thr = args.freq_max_threads_arg;
    char *folder = NULL;
    int len_i;
    char *str_opt = NULL;
    
    folder = strdup (folder_arg);
    
    /**< @brief If the folder name ended with a slash, it will be removed.*/
    if (folder[strlen (folder) - 1] == '/')
      folder[strlen (folder) - 1] = '\0';
		
    /**< @brief If the input argument freq_max_threads is less then 1 give an error message.*/
    if (!(f_max_thr > 0))
      ERROR (MAX_THR_ERR, "Parameter --freq-max-threads must be greater than zero!");
	  
    /**< @brief Get the hashtable with all the words and counters from all 
     * the locals hashtables using threads and show the word histogram.
     */
    m_thread (folder, f_max_thr, FREQ_OPTION, NULL);
    
    /**< @brief Get the length (number of digits) of the integer argument f_max_thr.*/
    len_i = num_digits (f_max_thr);
    
    str_opt =  (char *) MY_MALLOC (sizeof (char) * (strlen (folder_arg) + len_i + 35));
    sprintf (str_opt, "--freq-folder %s --freq-max-threads %d", folder_arg, f_max_thr);
    out_stderr (str_opt, t_start);
    MY_FREE (str_opt);
  }
 
 /** @brief Top_errors option.*/
  if (args.top_errors_given && args.top_spell_given && args.top_max_threads_given) {
    char *foldername = args.top_errors_arg;
    char *dictionary = args.top_spell_arg;
    int numThreads = args.top_max_threads_arg;
    int len_i;
    char *str_opt = NULL;
    
    mainTopErrors(foldername, dictionary, numThreads);
    
    len_i = num_digits (numThreads);
    
    str_opt =  (char *) MY_MALLOC (sizeof (char) * (strlen (foldername) + strlen (dictionary) + len_i + 46));
    sprintf (str_opt, "--top-errors %s --top-spell %s --top-max-threads %d", foldername, dictionary, numThreads);
    out_stderr (str_opt, t_start);
    MY_FREE (str_opt);
  }

  cmdline_parser_free (&args);
  return 0;
}
